﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Audio;
using System.Xml.Serialization;
using System.IO;
using Tomahawk.Serialization;
using Tomahawk.Serialization.SerialBoost;

namespace Tomahawk.Resources.Audio
{
    public sealed class AudioBank : IDisposable
    {
        #region Serializable Properties
        public class WaveBankResource
        {
            [XmlAttribute("Name")]
            public string name = "";

            [XmlAttribute("Resource")]
            public string resource = "";

            [XmlAttribute("Streamed")]
            public bool streamed = false;

            [XmlAttribute("Offset")]
            public int offset = 0;

            [XmlAttribute("PacketSize")]
            public short packetSize = 64;
        }

        public class SoundBankResource
        {
            [XmlAttribute("Name")]
            public string name = "";

            [XmlAttribute("Resource")]
            public string resource = "";
        }

        [XmlElement("WaveBanks")]
        public SerializableList<WaveBankResource> waveBankResources = new SerializableList<WaveBankResource>();
        
        [XmlElement("SoundBanks")]
        public SerializableList<SoundBankResource> soundBankResources = new SerializableList<SoundBankResource>();
        #endregion

        #region Properties
        private bool ready = false;
        public bool Ready { get { return ready; } }

        private Dictionary<string, WaveBank> waveBanks = new Dictionary<string, WaveBank>(StringComparer.OrdinalIgnoreCase);
        private Dictionary<string, SoundBank> soundBanks = new Dictionary<string, SoundBank>(StringComparer.OrdinalIgnoreCase);
        #endregion

        #region Initialization
        public bool Initialize(AudioEngine engine)
        {
            if (ready || engine == null)
            {
                return false;
            }
            
            try
            {
                foreach (WaveBankResource desc in waveBankResources)
                {
                    if (desc != null)
                    {
                        if (desc.streamed)
                        {
                            waveBanks.Add(desc.name, new WaveBank(engine, desc.resource, desc.offset, desc.packetSize));
                        }
                        else
                        {
                            waveBanks.Add(desc.name, new WaveBank(engine, desc.resource));
                        }
                    }
                }

                foreach (SoundBankResource desc in soundBankResources)
                {
                    soundBanks.Add(desc.name, new SoundBank(engine, desc.resource));
                }

                engine.Update();

                ready = true;
            }
            catch (Exception inner)
            {
                throw new Exception("Error loading audio bank", inner);
            }

            return true;
        }
        #endregion

        #region Audio Playback
        public Cue GetCue(string bank, string name)
        {
            SoundBank soundBank = null;
            if (soundBanks.TryGetValue(bank, out soundBank))
            {
                return soundBank.GetCue(name);
            }

            return null;
        }
        #endregion

        #region Serialization
        public void Serialize(string file)
        {
            using (FileStream fs = new FileStream(file, FileMode.Create, FileAccess.Write))
            {
                XmlSerializer x = new XmlSerializer(typeof(AudioBank));
                x.Serialize(fs, this);
            }
        }

        public static AudioBank Deserialize(string file)
        {
            using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
            {
                XmlSerializer x = new XmlSerializer(typeof(AudioBank));
                return (AudioBank)x.Deserialize(fs);
            }
        }
        #endregion

        #region IDisposable Members
        public void Dispose()
        {
            foreach (KeyValuePair<string, WaveBank> pair in waveBanks)
            {
                pair.Value.Dispose();
            }

            foreach (KeyValuePair<string, SoundBank> pair in soundBanks)
            {
                pair.Value.Dispose();
            }
        }
        #endregion
    }
}
